home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 3 / BBS in a box - Trilogy III.iso / Files / Prog / U-Z / VideoToolBox Folder / VideoToolboxSources / SetEntriesQuickly.c < prev    next >
Encoding:
Text File  |  1993-03-16  |  40.5 KB  |  1,073 lines  |  [TEXT/KAHL]

  1. /*
  2. SetEntriesQuickly.c
  3.  
  4. Load the clut as quickly as possible. SetEntriesQuickly has not been tested on
  5. all the devices that it's supposed to support. Run TimeVideo for a thorough
  6. test.
  7.  
  8. WARNING: This file REQUIRES 68020 or better, and includes a #pragma option
  9. that forces the compiler to assume its presence. So you should call Gestalt 
  10. to make sure you've got a 68020 before calling this subroutine. (This requirement
  11. could be eliminated by rewriting a few nonessential assembly language instructions
  12. to be compatible with the 68000, but I (dgp) am not sufficiently fluent
  13. in assembly to be able to do that quickly.)
  14.  
  15. This has been written by many people, and the "final" result has not been tested
  16. on all the computers and video devices it's meant to support. The best test is
  17. simply to run TimeVideo, which gives it a thorough workout on all of your
  18. computer's video devices. Please send the report "TimeVideo results" to
  19. denis_pelli@isr.syr.edu, and I'll add your test results to "Video synch"
  20. (section E). Naturally, we'll try to fix bugs as they are discovered.
  21.  
  22. Some Macintosh video drivers are poorly written; they take too long (more than a
  23. frame time) to load the clut. This is makes it impossible to do clut animation
  24. for temporal modulation etc., for which one needs to be able to reload the clut
  25. on each frame. At one time many of us thought that the limitation was in
  26. hardware, in the RAMDAC, but we were wrong. Raynald Comptois disassembled
  27. several video drivers and wrote his own programs to load the clut quickly, and
  28. his programs manage to do it within a frame. Raynald was kind enough to share
  29. his code with me. I passed it on to Peter Lennie and Bill Haake, who polished
  30. it, making it compatible with the 68040 processor, and added support for more
  31. cards. I polished their work, made the routines self contained, adding a
  32. "device" argument to allow use in Macs that have more than one video device, and
  33. quickly figuring out all the key parameters (mode, pixelSize, DAC size, and clut
  34. size). There are now two alternative front ends: SetEntriesQuickly() for new
  35. users, and macltset() for backward compatibility with programs that used
  36. Raynald's original routines. This modularity has increased runtime only
  37. slightly, a fraction of a millisecond.
  38.  
  39. New drivers are hard to write, since they must directly address the registers of
  40. the video card, which are unique to each video card and undocumented. So the
  41. author of a driver must disassemble the original video driver and figure things
  42. out on his or her own. A lot of work.
  43.  
  44. SetEntriesQuickly is unlike the standard video drivers in the following ways:
  45. 1. SetEntriesQuickly always takes less than 2 ms to load the whole clut. 
  46. Some video drivers, e.g. for Apple's 8•24 card, often take several frames to 
  47. finish loading the clut.
  48. 2A. SetEntriesQuickly does not wait for VBL, so a visible glitch may appear on
  49. the current frame, at least on some older video cards. (You can prevent this
  50. glitch by calling SetEntriesQuickly only at blanking time. Use a VBL task or
  51. WaitForBlanking().) If you write to the tables out of synch with the VBL you get
  52. glitches on most cards.  Some new cards seem to have dual ported memory, 
  53. as does the Quadra, so
  54. you can write at any point in the frame cycle without noticeable glitch, though
  55. of course you may for other reasons want to synch the update.
  56. 2B. A flag argument "waitForNextBlanking" is provided, but at present this
  57. option is only supported for the Toby video card.
  58. 3. SetEntriesQuickly ignores the gamma table, yielding the same result as using
  59. the standard video driver with an uncorrected gamma table. E.g. after calling
  60. GDUncorrectedGamma(device).
  61. 4. SetEntriesQuickly ignores the setting of the gray/color mode bit, always 
  62. assuming color.
  63. 5. SetEntriesQuickly() does nothing and returns and error if the arguments
  64. specify loading of an out-of-range index. This is contrary to the Apple
  65. specification for the SetEntries Toolbox subroutine that when the clut index 
  66. is out of range the driver implementing a
  67. setEntries control call should wrap around within the legal index range.
  68. 6. SetEntriesQuickly() does nothing and returns an error if the "count"
  69. specifies zero entries. This is contrary to the Apple specification that a
  70. setEntries control call with a count corresponding to zero entries will result
  71. in loading of entries specified by the "value" field of the ColorSpec.
  72. 7. SetEntriesQuickly does not have immediate access to the video driver's
  73. private tables. Therefore the first time you call SetEntriesQuickly() for a 
  74. particular device there is an extra delay of about 1 ms while some key
  75. information is ferreted out. That information is cached, so subsequent calls
  76. for the same device will be fast, spending most of their time loading the clut.
  77.  
  78. Two front ends are provided, for compatibility with two distinct traditions:
  79.  
  80. OSErr SetEntriesQuickly(GDHandle device,short start,short count,ColorSpec *table);
  81.  
  82. SetEntriesQuickly() uses the same calling convention as the VideoToolbox routine
  83. GDSetEntries() and, except for adding the GDHandle argument to specify the
  84. device, is also the same as Apple's SetEntries() Color Manager routine,
  85. documented in Inside Macintosh V-143. Apple specifies special behavior when
  86. count==-1, but we don't support that here and simply return with an error. I
  87. suggest that new users use SetEntriesQuickly. "start" is the index of the first
  88. clut entry to load, and should be greater than or equal to zero. "count" is the
  89. number of entries to load, minus 1. (Yes, "minus 1", that's Apple's convention.)
  90. "table" is a Colorspec array. (Each ColorSpec element is a structure consisting
  91. of a two-byte "value", which is not used, and a 6-byte "rgb", which, in turn is
  92. a structure of three 16-bit unsigned short ints: red, green, and blue. Apple's
  93. convention is that the MOST SIGNIFICANT BITS of the 16-bit color values are
  94. used. It is good practice in your programs to provide full 16-bit values, so
  95. that when you upgrade to fancier video cards with more-than-eight bit DACs your
  96. programs will benefit from the extra precision without needing any change.
  97. Returns zero if successful, nonzero if unsuccessful, i.e. illegal arguments.
  98.  
  99. short macltset(GDHandle device,short start
  100.     ,short* red,short* green,short* blue,short count1);
  101.  
  102. macltset() uses a calling convention established by Raynald Comtois, and
  103. provides backward compatibility with older programs. "red", "green", and "blue"
  104. are arrays of 16-bit unsigned short ints, of which the LEAST SIGNIFICANT BITS
  105. are used. "start" is the index of the first entry to change. "count1" is the
  106. number of entries to change (contrary to Apple's convention).
  107.  
  108. Both front ends use the same general-purpose subroutine: LoadClut(), which in
  109. turn calls the hardware-specific routine appropriate to the particular video 
  110. device being used.
  111.  
  112. The useMostSignificantBits bit of the "flags" argument specifies whether to use 
  113. Apple's convention (for users of SetEntriesQuickly) or Raynald's convention 
  114. (for users of macltset).
  115.  
  116. At the moment all the supported video cards have 8-bit DACs, except the RasterOps
  117. ProColor 32, which has 9-bit DACs. If the useMostSignificantBits flag is true
  118. then you don't need to worry, as the least significant bit of the 9-bit DAC
  119. simply picks up the next lower bit from your numbers, giving you a tad more
  120. precision. However, if useMostSignificantBits flag is false then, in order to
  121. use the full range of the DAC you must make all your numbers twice as big, or --
  122. cludge time! -- set the useOnly8Bits flag, to request that your 8-bit numbers be
  123. multiplied by two, allowing you to use the whole range of the DAC without
  124. changing the rest of your program, but wasting the DAC's least significant bit
  125. by setting it permanently to zero.
  126.  
  127. SUSPENDING INTERRUPTS. If you wish, the low-level routines will suspend
  128. interrupts while loading the clut. Presumably Raynald had his reasons for
  129. implementing this, so this "feature" is enabled when you use macltset(). Peter
  130. Lennie writes, "The switch to uninterruptable processor during the write is, I
  131. think, out of the original drivers (though I'm not absolutely sure).  I imagine
  132. it's to avoid display glitches that would result from some higher priority
  133. interrupt suspending a clut rewrite somewhere in the middle." However, I
  134. don't see any advantage to suspending interrupts, and believe that there is a
  135. significant downside if you are trying to keep track of the VBL interrupts on
  136. several video cards, since suspending interrupts for 1 or 2 ms might be long
  137. enough to miss a whole VBL interval. Thus SetEntriesQuickly disables this
  138. "feature". However, this is not a philosophical debate. We all agree that
  139. interrupts should be suspended if doing otherwise would occasionally result in a
  140. visible glitch. Does anyone know?
  141.  
  142. OSErr WaitForNextBlanking(GDHandle device);
  143.  
  144. Waits for beginning of next blanking interval. Currently this supports only the
  145. Toby card (Apple's original video card, now obsolete).
  146.  
  147. SPEED. SetEntriesQuickly() is self contained. You simply give it the GDHandle of
  148. your video device (as returned, e.g. by GetScreenDevice), and tell it what you
  149. want to do to the clut. In order to do this for you it needs to figure out a
  150. bunch of stuff about your video device. This research takes time; the first time
  151. you call it for a particular device it takes on the order of 1 ms to look up
  152. stuff. However, much of this stuff is saved in a cache, for each device, and can
  153. be retrieved quickly next time. Some users might be concerned that all this
  154. folderol is excessive for a fast operation that we may want to do at interrupt
  155. time. My answer is that the overhead is modest compared to the 1 or 2 ms
  156. required to actually load the clut, which, in my view, is too long a time to
  157. keep interrupts suspended anyway. My changes to the assembly language routines
  158. to deal with variable elementSpacing and using most- or least-significant bits
  159. have added extra operations. However, it is important to realize that the time
  160. penalty is insignificant because fast processors like the 68020, 68030, 68040
  161. are primarily limited by the time to access memory; by overlapping instruction
  162. execution they may lose almost no time at all doing register-to-register
  163. arithmetic. The new operations do not involve any new memory access.
  164.  
  165. The coding of the LoadClut "driver" routines is a compromise between the needs
  166. of SetEntriesQuickly and macltset, which both use them. I decided not to write
  167. separate clut loading loops for the two cases (use most vs least significant
  168. bits). I believe (but have not tested) that adding a register offset instead of
  169. using an autoincrement instruction incurs essentially no time penalty because
  170. the processor automatically overlaps the execution of such instructions. So I
  171. think that SetEntriesQuickly is running flat out, and don't see any prospect of
  172. speeding it up significantly. On the other hand, I suspect that fetching the
  173. least significant byte by doing a byte access to an odd address (for macltset)
  174. does slow things down perhaps 30% (though I haven't timed it) over doing a word
  175. access to an even address, as Raynald had originally coded it. If that speed
  176. loss is unacceptable, then one could insert an if(flags&useMostSignificantBits)
  177. statement into the relevant subroutine and write two separate loops optimized
  178. for the two cases. My guess is that the current compromise will be acceptable to
  179. all users.
  180.  
  181. NOTE: The first call of SetEntriesQuickly for each device is slow, but some
  182. stuff is cached so subsequent calls will be quick. The implication is that
  183. programs that use SetEntriesQuickly ought to call it once just for practice (to
  184. get the cache loading over with) before using it in a situation where speed
  185. matters.
  186.  
  187. REQUIRES 68020 or better.
  188.  
  189. IMPROVEMENTS:
  190. It is hoped that others will add to the functionality of this routine. Please
  191. share your enhancements with others by sending them to denis_pelli@isr.syr.edu
  192. for inclusion in the VideoToolbox.
  193.  
  194. Those wishing to support new video devices should begin by buying and reading
  195. Apple's Designing Cards and Drivers, 3rd Ed., Addison-Wesley, and then use the
  196. VideoToolbox utility GetVideoDrivers to copy all your drivers into resource
  197. files, and use ResEdit with CODE editor to peruse them. The ResEdit CODE editor
  198. is a public domain file distributed by:
  199. Ira L. Ruben
  200. Apple Computer, Inc.
  201. 20525 Mariani Ave., MS: 37-A
  202. Cupertino, Ca. 95014
  203. Ira@Apple.Com
  204. ftp.apple.com:/dts/mac/tools/resedit/resedit-2-1-code-editor...
  205.  
  206. It is logical that we identify the video card by the card name,
  207. GDCardName(driver), but in fact getting the card name is very slow (1.5 ms)
  208. whereas getting the driver name is fast, GDName(driver), and would be
  209. sufficiently unique for our purposes. (E.g. the Toby and TFB video cards have
  210. the same driver, and our code works for both cards.) Note that GDName() returns
  211. the address of a pascal string that should not be modified.
  212.  
  213. KNOWN BUGS:
  214. Has not been tested on all the video devices that are supposed to be supported.
  215. Please run the demo TimeVideo, and send the results file to denis_pelli@isr.syr.edu
  216.  
  217. The Quadra code requires that start==0. This could probably be figured out and
  218. fixed pretty easily if someone took the time to do so.
  219.  
  220. These routines do not wait for the vertical blanking interval before loading the
  221. clut. On many video devices this results in visible hash on the screen. I (dgp)
  222. consider this a bug, but, for most of these devices I don't know how to wait for
  223. the end of frame, short of setting up an interrupt. (Just about every video card
  224. has a bit that one could monitor, but its address is usually undocumented.) Some
  225. devices may be ok, because of dual-ported RAMDAC memory, or hardware buffering
  226. of the clut-load request. Check this out on your device: try TimeVideo.
  227.  
  228. HISTORY:
  229. 8/24/92 Original setcardbase and macltset provided by Raynald Comtois
  230. (raco@wjh12.harvard.edu) to Denis Pelli.
  231.  
  232. 10/2/92 Bill Haake added code for the RasterOps ProColor 32, which has 9-bit
  233. DACs and 9-bit entries in the lookup table.
  234.  
  235. 10/1/92 Peter Lennie added code for Quadra internal video.  No provision for
  236. changing the start position in the table, (I couldn't find any relevant
  237. disassembly) so 'start' is ignored, and you should write the whole table.
  238.     
  239. 9/30/92 Bill Haake & Peter Lennie modified the code for the 8x24 card
  240. and the 8x24GC to make it a) work properly in 32-bit mode. b) to fix a bug
  241. (feature?) of the original drivers that prevented the cards running on a Quadra.
  242. The drivers exploit 'byte-smearing' on the 68020 and 68030 (Tech Note 282). This
  243. means that one can move a byte to the lowest byte address of the data register
  244. on the card, when one actually wants to put it at address+3 (!!). The functions
  245. work for all the cards (except toby, which hasn't been tested) and on internal
  246. video in both 24 and 32 bit mode on Quadra 700/950, IIfx or ci running system
  247. 7.0.1.
  248.     
  249. 9/28/92 Peter Lennie added the function findcard.
  250.  
  251. 11/23/92 Denis Pelli (dgp) eliminated all globals because they implicitly
  252. assumed that there is only one video device. All routines now accept a GDHandle
  253. specifying which video device. Simplified the logic of GetCardBase(), minimizing
  254. the dependence on card type.
  255.  
  256. 11/25/92 dgp When USE_MSB is true all the routines now use the most significant
  257. bits of the 16-bit elements of the user-supplied color tables. When it is false
  258. the least significant bits are used. This is mostly implemented by offseting the
  259. table pointers by one byte and only reading the desired byte. •Generalized
  260. macltset() to work with tables that have an arbitrary element spacing. This
  261. allows it to work with both with Raynald's convention of three arrays of shorts,
  262. and the Apple convention of a ColorSpec array, each element of which consists of
  263. red, green, blue, and value (which is not used). •Added alias "Toby frame buffer
  264. card" for tobycard.
  265.  
  266. 11/27/92 dgp Broke out the code for each card into separate subroutines. This
  267. allows optimal register assignment for each routine, and makes it easier to read
  268. the THINK C disassembler output. The runtime overhead of loading and unloading
  269. the stack is negligible, and could be eliminated entirely by putting all the
  270. parameters in a structure and passing a pointer to it. •Added a flag,
  271. suspendInterrupts, to make interrupt suspension optional since it may be
  272. undesirable in some applications. (Blocking interrupts for 1 ms could cause you
  273. to miss the interrupt from a video card, especially if you are trying to keep
  274. track of interrupts on several video cards at once.)
  275.  
  276. 11/30/92 dgp Wrote TestCluts, which reads back the clut and checks all
  277. values, and used it to test SetEntriesQuickly() on Quadra 950 internal video,
  278. Mac IIci internal video, hirescard, "Toby frame buffer card", and 8•24 card at
  279. all depths, for both 24- and 32-bit addressing. Toby card was tested on 68020,
  280. 68030, and 68040 processors. •Wrote documentation. •Replaced compile-time constants
  281. USE_MSB and PRO_8BITS by runtime flags passed as arguments. •Added WaitForNextBlanking()
  282. based on code from VideoTFB.c.
  283.  
  284. 12/3/92 dgp Incorporated Peter Lennie's corrections and additions to the comments above.
  285.  
  286. 12/8/92 dgp Added missing "case" to switch in WaitForNextBlanking.
  287.  
  288. 12/13/92 dgp Changed erroneous "&d" to "%d" in a printf. Added some comments to
  289. the documentation above.
  290.             
  291. 12/15/92 dgp Now get mode from device record and leave it in standard form, 
  292. i.e. with the 0x80 bit set, and only strip off that bit when actually necessary,
  293. e.g. in LoadClutMacIIci. This allows comparison with Apple's predefined mode
  294. values, oneBitMode, etc., though that is done here at present. However, ordered
  295. comparisons would give more intuitive results if mode were declared as long, so
  296. that the sign bit would not be set.
  297.  
  298. 12/30/92 dgp Make sure routines return zero when there's no error.
  299.  
  300. 1/6/93 dgp Handle GDGetGamma failure gracefully, just guess at the dacSize. 
  301.  
  302. 2/15/93    dgp Rewrote nonworking LoadClutToby in C, and made it work. Rewrote
  303. nonworking LoadClutx824 in C, and made it work. Fixed sixteenBitMode in
  304. LoadClutQuadra. Use new SwapPriority instead of Get/SetPriority.
  305.  
  306. 2/20/93    dgp    Translated LoadClutGCx824 to C. (It was ignoring the start value.)
  307.  
  308. 3/4/93    dgp    Added macIIsi to list of supported cards, since it uses the same
  309. driver as the Mac IIci. Changed definitions of string types slightly to allow
  310. compilation of this file as a code resource. However, the assembly code
  311. uses more registers than are available to a code resource.
  312. */
  313. #include "VideoToolbox.h"
  314. #include <assert.h>
  315. #define USE_ONLY_8_BITS_IN_MACLTSET 0    // 1 to use RasterOps ProColor32 as an 8-bit DAC.
  316. #define C_CODE 1    // use C code to load clut, instead of assembly.
  317.                     // The C code is only about 10% slower than the assembly, and
  318.                     // is otherwise equivalent.
  319.                     // As of 11/30/92 this only affects the code for the hirescard.
  320. #pragma options(assign_registers,honor_register,redundant_loads,defer_adjust)
  321. #pragma options(global_optimizer,gopt_induction,gopt_loop,gopt_cse,gopt_coloring)
  322. #pragma options(mc68020)
  323.  
  324. // These are the five user-callable routines:
  325. OSErr WaitForNextBlanking(GDHandle device);
  326. OSErr SetEntriesQuickly(GDHandle device,short start,short count,ColorSpec *table);
  327. short macltset(GDHandle device,short start
  328.     ,short* red,short* green,short* blue,short count1);
  329. short GetCardType(GDHandle device);
  330. char *GetCardBase(GDHandle device);
  331.  
  332. /*
  333. I suggest keeping the following information private to this file. In principle
  334. you could publish these card types and use them in your programs. However, in
  335. practice, I cannot see any point in doing so. If you need to identify the card
  336. name I suggest you simply use the string returned by GDCardName(device) in
  337. GDVideo.c of the VideoToolbox. (Don't forget to call DisposPtr() when you're
  338. through with the string.) Or use GDName(device), which returns the name of the
  339. card's driver, and is much quicker. If you simply want to know whether your
  340. video card is supported by SetEntriesQuickly.c then you can simply make sure
  341. that GetCardType() returns a nonzero type.
  342. */
  343. struct vtype {        /* associates card name and id */
  344.     char name[40];
  345.     short id;
  346. };
  347. enum {                /* card identifiers */
  348.     tobycard = 1,
  349.     hirescard,
  350.     macIIci,
  351.     macIIsi,
  352.     x824card,
  353.     x824GCcard,
  354.     quadra700,
  355.     quadra900,
  356.     quadra950,
  357.     procolor32
  358. };
  359. static struct vtype card[] = {    // card name & id        // Original author:
  360.     {"Toby frame buffer card",                tobycard},    // Raynald Comtois
  361.     {"Display_Video_Apple_TFB",                tobycard},    //     "      "
  362.     {"Mac II High-Resolution Video Card",    hirescard},    // Raynald Comtois
  363.     {"Macintosh II Built-In Video",            macIIci},    // Raynald Comtois
  364.     {"Macintosh A Built-In Video",            macIIsi},    //     "      "
  365.     {"Macintosh Display Card",                x824card},    // Raynald Comtois
  366.     {"Macintosh Display Card 8•24 GC",        x824GCcard},// Raynald Comtois
  367.     {"Macintosh E Built-In Video",            quadra700},    // Peter Lennie
  368.     {"Macintosh C Built-In Video",            quadra900},    //     "      "
  369.     {"Macintosh G Built-In Video",            quadra950},    //     "      "
  370.     {"ProColor 32",                            procolor32}    // Bill Haake
  371. };
  372. static char driverName[][40]=        // Not used at present.
  373. {
  374.     "\p.Display_Video_Apple_TFB"    // Apple “Toby frame buffer card”
  375.     ,"\p.Display_Video_Apple_HRVC"    // Apple “Mac II High-Resolution Video Card”
  376.     ,"\p.Display_Video_Apple_MDC"    // Apple 8•24 “Macintosh Display Card”
  377.     ,"\p.???"                        // Apple 8•24GC
  378.     ,"\p.Display_Video_Apple_RBV1"    // Mac IIci and IIsi built-in video
  379.     ,"\p.Display_Video_Apple_DAFB"    // Quadra 700, 900, 950 built-in video
  380.     ,"\p.???"                        // Radius ProColor 32
  381. };
  382. enum {                        // Flags passed to LoadClut().
  383.     suspendInterrupts=1,
  384.     useMostSignificantBits=2,
  385.     useOnly8Bits=4,
  386.     waitForNextBlanking=8
  387. };
  388. enum{quadraNonzeroStart=111};    // value returned as error.
  389.     
  390. short LoadClut(GDHandle device,short start,short count
  391.     ,short* red,short* green,short* blue,long elementSpacing,short flags);
  392. OSErr LoadClutProColor(short start,short count,char *r,char *g,char *b
  393.     ,long elementSpacing,short mode,short pixelSize,short ctSize
  394.     ,char *cardBase,short flags);
  395. OSErr LoadClutQuadra(short start,short count,char *r,char *g,char *b
  396.     ,long elementSpacing,short mode,short pixelSize,short ctSize
  397.     ,char *cardBase,short flags);
  398. OSErr LoadClutMacIIci(short start,short count,char *r,char *g,char *b
  399.     ,long elementSpacing,short mode,short pixelSize,short ctSize
  400.     ,char *cardBase,short flags);
  401. OSErr LoadClutHiRes(short start,short count,char *r,char *g,char *b
  402.     ,long elementSpacing,short mode,short pixelSize,short ctSize
  403.     ,char *cardBase,short flags);
  404. OSErr LoadClutx824(short start,short count,char *r,char *g,char *b
  405.     ,long elementSpacing,short mode,short pixelSize,short ctSize
  406.     ,char *cardBase,short flags);
  407. OSErr LoadClutx824GC(short start,short count,char *r,char *g,char *b
  408.     ,long elementSpacing,short mode,short pixelSize,short ctSize
  409.     ,char *cardBase,short flags);
  410. OSErr LoadClutToby(short start,short count,char *r,char *g,char *b
  411.     ,long elementSpacing,short mode,short pixelSize,short ctSize
  412.     ,char *cardBase,short flags);
  413. /******************************************************************************/
  414. /*
  415. The arguments start, count, and table are the same as for the Color Manager call
  416. SetEntries(), documented in Inside Macintosh V-143. (Except that a count==-1 is
  417. considered illegal here.) Apple's ideosyncratic convention is that "count" is
  418. "zero-based", meaning that it is one less than the number of clut entries that
  419. you want to modify. "count" must be at least zero. Returns zero if successful,
  420. nonzero if unsuccessful, i.e. illegal arguments.
  421. */
  422. OSErr SetEntriesQuickly(GDHandle device,short start,short count,ColorSpec *table)
  423. {
  424.     short flags=useMostSignificantBits;
  425.     //flags+=suspendInterrupts;        // Optional, no
  426.     //flags+=waitForNextBlanking;    // Optional, no
  427.  
  428.     return LoadClut(device,start,count
  429.         ,(short *)&table[0].rgb.red,(short *)&table[0].rgb.green
  430.         ,(short *)&table[0].rgb.blue,sizeof(table[0]),flags);
  431. }
  432. /******************************************************************************/
  433. short macltset(GDHandle device,register short start
  434.     ,short* red,short* green,short* blue,short count1)
  435. {
  436.     short flags=0;
  437.     flags+=suspendInterrupts;        // Optional, yes
  438.     #if USE_ONLY_8_BITS_IN_MACLTSET
  439.         flags+=useOnly8Bits;        // Optional
  440.     #endif
  441.     //flags+=waitForNextBlanking;    // Optional, no
  442.     
  443.     return LoadClut(device,start,count1-1,red,green,blue,sizeof(red[0]),flags);
  444. }
  445. /******************************************************************************/
  446. /*
  447. The first call to GetCardType for a particular device takes 1.5-3 ms, depending
  448. on your computer's speed, because it takes Apple's Slot Manager a long time to
  449. get the card name. However, GetCardType's answers are cached so subsequent calls
  450. for a previously queried device will be fast <100 µs.
  451. */
  452. short GetCardType(GDHandle device)    // returns card type, if known, or zero if not.
  453. {
  454.     register short i;
  455.     short type;
  456.     char *name;
  457.     static GDHandle deviceCache[MAX_SCREENS];
  458.     static short typeCache[MAX_SCREENS];
  459.     
  460.     // Do we already know the answer? Check the cache.
  461.     for(i=0;i<MAX_SCREENS;i++)
  462.         if(device==deviceCache[i])return typeCache[i];
  463.     // Get card name, see if it's in our list of known cards
  464.     name=GDCardName(device);
  465.     type=0;
  466.     for (i=0; i<sizeof(card)/sizeof(card[0]); i++){
  467.         if(strcmp(name,card[i].name)==0){
  468.             type=card[i].id;
  469.             break;
  470.         }
  471.     }
  472.     DisposePtr(name);
  473.     // Save answer in cache.
  474.     for(i=0;i<MAX_SCREENS;i++){
  475.         if(deviceCache[i]==0){
  476.             typeCache[i]=type;
  477.             deviceCache[i]=device;
  478.             break;
  479.         }
  480.     }
  481.     return type;
  482. }
  483. /******************************************************************************/
  484. long internalVideoBase:0xDD8;    // Undocumented System global
  485.  
  486. char *GetCardBase(GDHandle device)
  487. {
  488.     long cardBase,slot;    /* slot must be declared long */
  489.     short type;
  490.     
  491.     slot=GetDeviceSlot(device);
  492.     if(slot==0){
  493.         // Built-in video, not in a NuBus slot.
  494.         // E.g.: macIIci,quadra700,quadra900,quadra950
  495.         #if 1
  496.             // This C is equivalent to Raynald's assembly code below.
  497.             cardBase = *(long *)(internalVideoBase + *(long *)internalVideoBase + 56);
  498.         #else
  499.             asm {
  500.                 move.l 0xDD8,a0        /* get card base address */
  501.                 adda.l (a0),a0
  502.                 move.l 56(a0),a1
  503.                 move.l a1,cardBase
  504.             }
  505.         #endif
  506.     }else{
  507.         // Video card in NuBus slot
  508.         type=GetCardType(device);
  509.         switch(type){
  510.             case x824GCcard:
  511.                 cardBase = slot << 28;    /* a superslot */
  512.                 break;
  513.             case tobycard:
  514.                 cardBase=0x01100000*slot | 0xF0000000;
  515.                 break;
  516.             case hirescard:
  517.             case procolor32: /* RasterOps Board */
  518.             case x824card:
  519.             default:
  520.                 cardBase = (slot<<24) | 0xF0000000;
  521.                 break;        
  522.         }
  523.     }
  524.     return (char *)cardBase;
  525. }
  526. /******************************************************************************/
  527. short LoadClut(GDHandle device,short start,short count
  528.     ,short* red,short* green,short* blue
  529.     ,long elementSpacing,short flags)
  530. {
  531.     char *cardBase;
  532.     short type=0;        // type of card
  533.     short ctSize;         // entries, minus 1, in the lookup table
  534.     short pixelSize;    // Log2L(ctSize+1)
  535.     short mode;            // 0x80+Log2L(pixelSize)
  536.     OSErr error;
  537.     GammaTbl *gammaTblPtr=NULL;
  538.     short dacSize=8;    // bits (typically 8 or 9)
  539.     static GDHandle deviceCache[MAX_SCREENS];
  540.     static short typeCache[MAX_SCREENS],dacSizeCache[MAX_SCREENS];
  541.     int i;
  542.     short inCache=0;
  543.     
  544.     assert(sizeof(ctSize)==2);
  545.     assert(sizeof(*cardBase)==1);
  546.     if(device==NULL)return 1;
  547.     // This setting up, before actually loading the clut, takes 1.7 ms 
  548.     // the first time a particular device
  549.     // is used, but, only 200 µs for each subsequent use of that device
  550.     // because we cache the key answers. Note that we are careful to
  551.     // only save things that won't change: the card type and the DAC size.
  552.     // The mode (and pixelSize and ctSize) could be changed by the user at any time
  553.     // by calling Apple's SetDepth() or the VideoToolbox's GDSetMode().
  554.     for(i=0;i<MAX_SCREENS;i++){    // Look in cache.
  555.         if(device==deviceCache[i]){
  556.             type=typeCache[i];
  557.             dacSize=dacSizeCache[i];
  558.             inCache=1;
  559.             break;
  560.         }
  561.     }
  562.     if(!inCache){        // Not in cache.
  563.         type=GetCardType(device);                    // Takes 1.5 ms
  564.         if(type==0)return 1;
  565.         error=GDGetGamma(device,&gammaTblPtr);        // Takes 200 µs.
  566.         if(error){
  567.             // printf("SetEntriesQuickly.c:LoadClut(): GDGetGamma() failed with error %d\n",error);
  568.             dacSize=8;
  569.         }else dacSize=gammaTblPtr->gDataWidth;
  570.         for(i=0;i<MAX_SCREENS;i++){                    // Save in cache.
  571.             if(deviceCache[i]==NULL){
  572.                 deviceCache[i]=device;
  573.                 typeCache[i]=type;
  574.                 dacSizeCache[i]=dacSize;
  575.                 break;
  576.             }
  577.         }
  578.     }
  579.     if(type==0)return 1;
  580.     cardBase=GetCardBase(device);
  581.     // Get device mode from GDevice record. Fast but unreliable if user 
  582.     // calls GDSetMode().
  583.     ctSize=(**(**(**device).gdPMap).pmTable).ctSize;
  584.     pixelSize=(**(**device).gdPMap).pixelSize;
  585.     mode=(**device).gdMode;
  586.     if(0){    // Get mode by asking the device itself. Reliable but slow.
  587.         // Conceivably one might be able to locate and directly access
  588.         // the driver's internal table.
  589.         error=GDGetMode(device,&mode,NULL,NULL);    // takes 0.3-0.6 ms.
  590.         pixelSize=1<<(mode&7);
  591.     }
  592.     
  593.     // Check range.
  594.     if(start>ctSize || start<0 || count+start>ctSize || count<0)return 1;
  595.         
  596.     if(waitForNextBlanking & flags){
  597.         WaitForNextBlanking(device);
  598.     }
  599.     // After the above setting up, actually loading 256x3 clut entries takes 1-2 ms.
  600.     red = (short *)StripAddress(red);
  601.     green = (short *)StripAddress(green);
  602.     blue = (short *)StripAddress(blue);
  603.     switch (type) {
  604.         // I packaged the code for each case into a separate subroutine
  605.         // in order to allow the THINK C compiler to optimally optimize each
  606.         // one independently. An important consideration is that the THINK C 5.04
  607.         // compiler disables most optimizations for any function that includes
  608.         // the "asm" directive anywhere within the function. Thus mixing C and assembly
  609.         // will result in inefficient C.
  610.         case procolor32:
  611.             error=LoadClutProColor(start,count,(char *)red,(char *)green,(char *)blue,
  612.                 elementSpacing,mode,pixelSize,ctSize,cardBase,flags);
  613.             break;
  614.         case quadra700:
  615.         case quadra900:
  616.         case quadra950:
  617.             error=LoadClutQuadra(start,count,(char *)red,(char *)green,(char *)blue,
  618.                 elementSpacing,mode,pixelSize,ctSize,cardBase,flags);
  619.             break;
  620.         case macIIci:
  621.         case macIIsi:
  622.             error=LoadClutMacIIci(start,count,(char *)red,(char *)green,(char *)blue,
  623.                 elementSpacing,mode,pixelSize,ctSize,cardBase,flags);
  624.             break;
  625.         case hirescard:
  626.             error=LoadClutHiRes(start,count,(char *)red,(char *)green,(char *)blue,
  627.                 elementSpacing,mode,pixelSize,ctSize,cardBase,flags);
  628.             break;
  629.         case x824card:
  630.             error=LoadClutx824(start,count,(char *)red,(char *)green,(char *)blue,
  631.                 elementSpacing,mode,pixelSize,ctSize,cardBase,flags);
  632.             break;
  633.         case x824GCcard:
  634.             error=LoadClutx824GC(start,count,(char *)red,(char *)green,(char *)blue,
  635.                 elementSpacing,mode,pixelSize,ctSize,cardBase,flags);
  636.             break;
  637.         case tobycard:
  638.             error=LoadClutToby(start,count,(char *)red,(char *)green,(char *)blue,
  639.                 elementSpacing,mode,pixelSize,ctSize,cardBase,flags);
  640.             break;
  641.     }
  642.     return error;
  643. }
  644. /******************************************************************************/
  645. OSErr LoadClutProColor(short start,register short count
  646.     ,register char *red,register char *green,register char *blue
  647.     ,register long elementSpacing
  648.     ,short mode,short pixelSize,short ctSize,char *cardBase,short flags)
  649. {
  650.     char mmuMode=true32b,priority=7;
  651.     register long bitShift;
  652.  
  653.     if(useMostSignificantBits & flags){
  654.         bitShift=9;
  655.     }else{
  656.         if(useOnly8Bits & flags) bitShift=1;
  657.         else bitShift=0;
  658.     }
  659.     if(suspendInterrupts & flags)SwapPriority(&priority);
  660.     SwapMMUMode(&mmuMode);
  661.     asm {
  662.         move.l cardBase,a1        /* get card base address */
  663.         adda.l    #0xf60000,a1    /* offset to control registers */        
  664.     @9    move.w    start,2(a1)        /* Set the index on the card */
  665.         move.w (red),d1
  666.         add.l elementSpacing,red
  667.         lsl.w bitShift,d1
  668.         move.w d1,14(a1)
  669.         move.w (green),d1
  670.         add.l elementSpacing,green
  671.         lsl.w bitShift,d1
  672.         move.w d1,14(a1)
  673.         move.w (blue),d1
  674.         add.l elementSpacing,blue
  675.         lsl.w bitShift,d1
  676.         move.w d1,14(a1)
  677.         addq.w    #1,start        /* Point to next entry in table */
  678.         dbf count,@9
  679.     }
  680.     SwapMMUMode(&mmuMode);
  681.     if(suspendInterrupts & flags)SwapPriority(&priority);
  682.     return 0;
  683. }
  684. OSErr LoadClutQuadra(short start,register short count
  685.     ,register char *red,register char *green,register char *blue
  686.     ,register long elementSpacing
  687.     ,short mode,short pixelSize,short ctSize,char *cardBase,short flags)
  688. {
  689.     char mmuMode=true32b,priority=7;
  690.  
  691.     if(start!=0){
  692.         //printf("LoadClutQuadra: start must be zero\n");
  693.         return quadraNonzeroStart;
  694.     }
  695.     if(!(useMostSignificantBits & flags)){
  696.         // Point to less significant byte of word.
  697.         red++;
  698.         green++;
  699.         blue++;
  700.     }
  701.     if(suspendInterrupts & flags)SwapPriority(&priority);
  702.     SwapMMUMode(&mmuMode);
  703.     if(mode!=sixteenBitMode)asm{
  704.         move.l cardBase,a1
  705.         lea 0x210(a1), a1
  706.         clr.l -16(a1)
  707.     @4    move.b (red),d1
  708.         add.l elementSpacing,red
  709.         move.l d1,(a1)
  710.         move.b (green),d1
  711.         add.l elementSpacing,green
  712.         move.l d1,(a1)
  713.         move.b (blue),d1
  714.         add.l elementSpacing,blue
  715.         move.l d1,(a1)
  716.         dbf count,@4
  717.     }else asm{
  718.     // In sixteenBitMode the clut addressing is weird.
  719.     // I arrived at the following solution by trial and error.
  720.     // It's a kludge, but is still fast enough. dgp.
  721.         move.l cardBase,a1
  722.         lea 0x210(a1), a1
  723.         clr.l -16(a1)
  724.     @44    move.b (red),d1
  725.         move.l d1,(a1)
  726.         move.b (green),d1
  727.         move.l d1,(a1)
  728.         move.b (blue),d1
  729.         move.l d1,(a1)
  730.  
  731.         move.b (red),d1
  732.         move.l d1,(a1)
  733.         move.b (green),d1
  734.         move.l d1,(a1)
  735.         move.b (blue),d1
  736.         move.l d1,(a1)
  737.  
  738.         move.b (red),d1
  739.         move.l d1,(a1)
  740.         move.b (green),d1
  741.         move.l d1,(a1)
  742.         move.b (blue),d1
  743.         move.l d1,(a1)
  744.  
  745.         move.b (red),d1
  746.         move.l d1,(a1)
  747.         move.b (green),d1
  748.         move.l d1,(a1)
  749.         move.b (blue),d1
  750.         move.l d1,(a1)
  751.  
  752.         move.b (red),d1
  753.         move.l d1,(a1)
  754.         move.b (green),d1
  755.         move.l d1,(a1)
  756.         move.b (blue),d1
  757.         move.l d1,(a1)
  758.  
  759.         move.b (red),d1
  760.         move.l d1,(a1)
  761.         move.b (green),d1
  762.         move.l d1,(a1)
  763.         move.b (blue),d1
  764.         move.l d1,(a1)
  765.  
  766.         move.b (red),d1
  767.         move.l d1,(a1)
  768.         move.b (green),d1
  769.         move.l d1,(a1)
  770.         move.b (blue),d1
  771.         move.l d1,(a1)
  772.  
  773.         move.b (red),d1
  774.         add.l elementSpacing,red
  775.         move.l d1,(a1)
  776.         move.b (green),d1
  777.         add.l elementSpacing,green
  778.         move.l d1,(a1)
  779.         move.b (blue),d1
  780.         add.l elementSpacing,blue
  781.         move.l d1,(a1)
  782.  
  783.         dbf count,@44
  784.     }
  785.     SwapMMUMode(&mmuMode);
  786.     if(suspendInterrupts & flags)SwapPriority(&priority);
  787.     return 0;
  788. }
  789. OSErr LoadClutMacIIci(register short start,register short count
  790.     ,register char *red,register char *green,register char *blue
  791.     ,register long elementSpacing
  792.     ,short mode,short pixelSize,short ctSize,char *cardBase,short flags)
  793. {
  794.     static char realstartindex[] = {0xFE, 0xFC, 0xF0, 0x00};
  795.     char priority=7;
  796.     
  797.     if(!(useMostSignificantBits & flags)){
  798.         // Point to less significant byte of word.
  799.         red++;
  800.         green++;
  801.         blue++;
  802.     }
  803.     if(suspendInterrupts & flags)SwapPriority(&priority);
  804.     mode&=7;
  805.     asm {
  806.         move.w mode,d1
  807.         add.b (realstartindex,a5,d1.w),start
  808.         move.l cardBase,a0        // get card base address
  809.         move.l a0,a1
  810. //        move.b #255,8(a0)        // not necessary
  811.         move.b start,(a0)
  812.         addq.l #4,a1
  813.     @3    move.b (red),d1
  814.         add.l elementSpacing,red
  815.         move.b d1,(a1)
  816.         move.b (green),d1
  817.         add.l elementSpacing,green
  818.         move.b d1,(a1)
  819.         move.b (blue),d1
  820.         add.l elementSpacing,blue
  821.         move.b d1,(a1)
  822.         dbf count,@3
  823.     }
  824.     if(suspendInterrupts & flags)SwapPriority(&priority);
  825.     return 0;
  826. }
  827. // High resolution video card
  828. //#define HRVCBase            0x80000
  829. #define HRVCClutAddrReg        0x940E0
  830. #define HRVCClutWDataReg    0x940E4
  831. //#define HRVCClutRDataReg    0x94054
  832. OSErr LoadClutHiRes(short start,register short count
  833.     ,register char *red,register char *green,register char *blue
  834.     ,register long elementSpacing
  835.     ,short mode,short pixelSize,short ctSize,char *cardBase,short flags)
  836. {
  837.     char *bytePtr;
  838.     char mmuMode=true32b,priority=7;
  839.     
  840.     if(!(useMostSignificantBits & flags)){
  841.         // Point to less significant byte of word.
  842.         red++;
  843.         green++;
  844.         blue++;
  845.     }
  846.     if(suspendInterrupts & flags)SwapPriority(&priority);
  847.     SwapMMUMode(&mmuMode);
  848.     #if C_CODE 
  849.         red+=count*elementSpacing;
  850.         green+=count*elementSpacing;
  851.         blue+=count*elementSpacing;
  852.         // We'll start with clut entry start+count, and work
  853.         // down from there to clut entry start. The clut address
  854.         // register counts down automatically.
  855.         *(cardBase+HRVCClutAddrReg)=~(ctSize-start-count);
  856.         bytePtr=cardBase+HRVCClutWDataReg;
  857.     #else
  858.      asm {
  859.         move.l elementSpacing,d0
  860.         mulu count,d0
  861.         add.l d0,red
  862.         add.l d0,green
  863.         add.l d0,blue
  864.         move.w ctSize,d1
  865.         sub.w start,d1
  866.         sub.w count,d1
  867.         not.b d1
  868.         move.l cardBase,a0
  869.         move.l a0,a1
  870.         add.l #HRVCClutAddrReg,a0
  871.         move.b d1,(a0)
  872.         add.l #HRVCClutWDataReg,a1
  873.         move.l a1,bytePtr
  874.     };
  875.     #endif
  876.     #if C_CODE
  877.         // This is the key loop. The THINK C compiler produces fast code
  878.         // only if there is no "asm" inside the function.
  879.         // The resulting C code is only about 10% slower than the assembly code.
  880.         elementSpacing= -elementSpacing;
  881.         do{
  882.             *bytePtr=~ *red;
  883.             red+=elementSpacing;
  884.             *bytePtr=~ *green;
  885.             green+=elementSpacing;
  886.             *bytePtr=~ *blue;
  887.             blue+=elementSpacing;
  888.         }while(--count>=0);
  889.     #else
  890.     asm{
  891.         // This loop is only 10% faster coded in assembly than in C, above.
  892.         // I suspect the main difference is that the C compiler does a test and
  893.         // branch instead of using the DBF instruction.
  894.         move.l bytePtr,a1
  895.         neg.l elementSpacing
  896.     @0    move.b (red),d1
  897.         add.l elementSpacing,red
  898.         not.b d1
  899.         move.b d1,(a1)
  900.         move.b (green),d1
  901.         add.l elementSpacing,green
  902.         not.b d1
  903.         move.b d1,(a1)
  904.         move.b (blue),d1
  905.         add.l elementSpacing,blue
  906.         not.b d1
  907.         move.b d1,(a1)
  908.         dbf count,@0
  909.     };
  910.     #endif
  911.     SwapMMUMode(&mmuMode);
  912.     if(suspendInterrupts & flags)SwapPriority(&priority);
  913.     return 0;
  914. }
  915. // Macintosh display card (8•24)
  916. //#define MDCVideoBase        0xA00
  917. #define MDCClutAddrReg        0x200200
  918. #define MDCClutDataReg        0x200204
  919. OSErr LoadClutx824(short start,register short count
  920.     ,register char *red,register char *green,register char *blue
  921.     ,register long elementSpacing
  922.     ,short mode,short pixelSize,short ctSize,char *cardBase,short flags)
  923. {
  924.     char mmuMode=true32b,priority=7;
  925.     register char *clut;
  926.     char *clutIndex;
  927.  
  928.     if(!(useMostSignificantBits & flags)){
  929.         // Point to less significant byte of word.
  930.         red++;
  931.         green++;
  932.         blue++;
  933.     }
  934.     if(suspendInterrupts & flags)SwapPriority(&priority);
  935.     SwapMMUMode(&mmuMode);
  936.     clut=cardBase+MDCClutDataReg+3;
  937.     clutIndex=cardBase+MDCClutAddrReg;
  938.     *clutIndex=start;
  939.     for(;count>=0;count--){
  940.         *clut=*red;
  941.         red+=elementSpacing;
  942.         *clut=*green;
  943.         green+=elementSpacing;
  944.         *clut=*blue;
  945.         blue+=elementSpacing;
  946.     }
  947.     SwapMMUMode(&mmuMode);
  948.     if(suspendInterrupts & flags)SwapPriority(&priority);
  949.     return 0;
  950. }
  951. // Macintosh display card 8•24 GC
  952. #define MDCgcClutAddrReg    0x6C00000
  953. #define MDCgcClutDataReg    0x6C00004
  954. OSErr LoadClutx824GC(short start,register short count
  955.     ,register char *red,register char *green,register char *blue
  956.     ,register long elementSpacing
  957.     ,short mode,short pixelSize,short ctSize,char *cardBase,short flags)
  958. {
  959.     char mmuMode=true32b,priority=7;
  960.     register long *clut;
  961.     char *clutIndex;
  962.  
  963.     if(!(useMostSignificantBits & flags)){
  964.         // Point to less significant byte of word.
  965.         red++;
  966.         green++;
  967.         blue++;
  968.     }
  969.     if(suspendInterrupts & flags)SwapPriority(&priority);
  970.     SwapMMUMode(&mmuMode);
  971.     clutIndex=cardBase+MDCgcClutAddrReg;
  972.     *clutIndex=start;
  973.     #if 0
  974.         clut=(long *)(cardBase+MDCgcClutDataReg);
  975.         for(;count>=0;count--){
  976.             *clut=(long)(*red)<<24;
  977.             red+=elementSpacing;
  978.             *clut=(long)(*green)<<24;
  979.             green+=elementSpacing;
  980.             *clut=(long)(*blue)<<24;
  981.             blue+=elementSpacing;
  982.         }
  983.     #else
  984.         asm {
  985.             move.l cardBase,a1
  986.             add.l #MDCgcClutDataReg,a1
  987.         @8    move.b (red),d1
  988.             add.l    elementSpacing,red
  989.             ror.l    #8,d1
  990.             move.l d1,(a1)
  991.             move.b (green),d1
  992.             add.l    elementSpacing,green
  993.             ror.l    #8,d1
  994.             move.l d1,(a1)
  995.             move.b (blue),d1
  996.             add.l elementSpacing,blue
  997.             ror.l    #8,d1
  998.             move.l d1,(a1)
  999.             dbf count,@8
  1000.         }
  1001.     #endif
  1002.     SwapMMUMode(&mmuMode);
  1003.     if(suspendInterrupts & flags)SwapPriority(&priority);
  1004.     return 0;
  1005. }
  1006. // Toby frame buffer
  1007. //#define    TFBBase            0x80000
  1008. //#define TFBBufMid            0x80008
  1009. //#define TFBBufLow            0x8000C
  1010. //#define    TFBIBase        0x8fffc
  1011. #define TFBClutWDataReg        0x90018
  1012. //#define TFBClutRDataReg    0x90028
  1013. #define TFBClutAddrReg        0x9001C
  1014. #define    TFBReadVSync        0xD0000
  1015. //#define    TFBReadVInt        0xD0004
  1016. //#define    TFBReadIntlc    0xD0008
  1017. //#define    TFBVIntEnable    0xA0000
  1018. //#define    TFBVIntDisable    0xA0004
  1019. OSErr LoadClutToby(short start,register short count
  1020.     ,register char *red,register char *green,register char *blue
  1021.     ,register long elementSpacing
  1022.     ,short mode,short pixelSize,short ctSize,char *cardBase,short flags)
  1023. {
  1024.     register long index;
  1025.     char mmuMode=true32b,priority=7;
  1026.     register char *clut,*clutIndex;
  1027.     short shift;
  1028.     
  1029.     if(!(useMostSignificantBits & flags)){
  1030.         // Point to less significant byte of word.
  1031.         red++;
  1032.         green++;
  1033.         blue++;
  1034.     }
  1035.     index=(count+1)*elementSpacing;
  1036.     red+=index;
  1037.     green+=index;
  1038.     blue+=index;
  1039.     shift=8-pixelSize;
  1040.     index=start+count+1;
  1041.     clut=cardBase+TFBClutWDataReg;
  1042.     clutIndex=cardBase+TFBClutAddrReg;
  1043.     if(suspendInterrupts & flags)SwapPriority(&priority);
  1044.     for(;count>=0;count--,index--){
  1045.         *clutIndex=(index<<shift)-1;
  1046.         red-=elementSpacing;
  1047.         *clut=~*red;
  1048.         green-=elementSpacing;
  1049.         *clut=~*green;
  1050.         blue-=elementSpacing;
  1051.         *clut=~*blue;
  1052.     }
  1053.     if(suspendInterrupts & flags)SwapPriority(&priority);
  1054.     return 0;
  1055. }
  1056.  
  1057. OSErr WaitForNextBlanking(GDHandle device)
  1058. // WaitForNextBlanking waits for the beginning of the next vertical blanking interval.
  1059. // Returns 0 if successful, or 1 if device is not supported.
  1060. {
  1061.     register long *blankingPtr;
  1062.  
  1063.     switch(GetCardType(device)){
  1064.     case tobycard:
  1065.         blankingPtr = (long *) ((char *)GetCardBase(device) + TFBReadVSync);
  1066.         while (*blankingPtr & 1L);    // if we're already blanking, wait till end.
  1067.         while (!(*blankingPtr & 1L)); // wait until beginning of blanking interval.
  1068.         return 0;
  1069.     default:
  1070.         return 1;
  1071.     }
  1072. }
  1073.